home *** CD-ROM | disk | FTP | other *** search
- /*
- File: DoSCSICommand.c
-
- Contains: his is a simple sample for the original SCSI manager that shows how to
- arbitrate for the SCSI bus and send a command using the original SCSI manager.
- This function is incomplete in that it does not support virtual memory and
- presumes a six-byte SCSI command block.
-
- This sample is further limited in that it always does polled reads.
-
- Calling Sequence:
- OSErr DoSCSICommand(
- unsigned short targetID,
- const Ptr scsiCommand,
- SCSIInstr requestTIB
- );
- The parameters have the following meaning:
-
- targetID The SCSI Bus ID of the target (0 .. 6). Note that this
- function can only access LUN zero.
- scsiCommand The SCSI Command Block (6 bytes).
- requestTIB The transfer request block.
-
- Return codes:
- noErr normal
- scCommErr no such device (selection error)
- scPhaseErr user data buffer was the wrong size for the transfer.
- You may merely have given a large buffer size to
- a variable-length request, such as Device Inquiry.
- sc... other SCSI error.
- statusErr Device returned "Check condition." The caller should
- issue a Request Sense SCSI Command to this device.
- controlErr Device returned "Busy"
- ioErr Other (serious) device status.
-
- Written by: Martin Minow
-
- Copyright: Copyright © 1992-1999 by Apple Computer, Inc., All Rights Reserved.
-
- You may incorporate this Apple sample source code into your program(s) without
- restriction. This Apple sample source code has been provided "AS IS" and the
- responsibility for its operation is yours. You are not permitted to redistribute
- this Apple sample source code as "Apple sample source code" after having made
- changes. If you're going to re-distribute the source, we require that you make
- it clear in the source that the code was descended from Apple sample source
- code, but that you've made changes.
-
- Change History (most recent first):
- 7/15/1999 Karl Groethe Updated for Metrowerks Codewarror Pro 2.1
-
-
- */
- #include <scsi.h>
- #include <Errors.h>
- #include <OSUtils.h>
- #include <Events.h>
-
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * SCSI command status (from status phase)
- */
- #define kScsiStatusGood 0x00 /* Normal completion */
- #define kScsiStatusCheckCondition 0x02 /* Need GetExtendedStatus */
- #define kScsiStatusConditionMet 0x04 /* For Compare Command? */
- #define kScsiStatusBusy 0x08 /* Device busy (self-test?) */
- #define kScsiStatusIntermediate 0x10 /* Intermediate status */
- #define kScsiStatusResConflict 0x18 /* Reservation conflict */
- #define kScsiStatusQueueFull 0x28 /* Target can't do command */
- #define kScsiStatusReservedMask 0x3e /* Vendor specific? */
-
- /*
- * This is the maximum number of times we try to grab the SCSI Bus
- */
- #define kMaxSCSIRetries 40 /* 10 seconds, 4 times/sec */
- /*
- * This test is TRUE if the SCSI bus status indicates "busy" (which is the case
- * if either the BSY or SEL bit is set).
- */
- #ifndef kScsiStatBSY
- #define kScsiStatBSY (1 << 6)
- #endif
- #ifndef kScsiStatSEL
- #define kScsiStatSEL (1 << 1)
- #endif
- #define ScsiBusBusy() ((SCSIStat() & (kScsiStatBSY | kScsiStatSEL)) != 0)
-
-
- OSErr DoSCSICommand(
- unsigned short targetID,
- const Ptr scsiCommand,
- Ptr requestTIB
- );
-
- OSErr DoSCSICommand(
- unsigned short targetID,
- const Ptr scsiCommand,
- Ptr requestTIB
- )
- {
-
- OSErr status; /* Final status */
- OSErr completionStatus; /* Status from ScsiComplete */
- short totalTries; /* Get/Select retries */
- short getTries; /* Get retries */
- short iCount; /* Bus free counter */
- unsigned long watchdog; /* Timeout after this */
- short stsByte; /* Status byte from device */
- short msgByte; /* Message byte from device */
-
- status = noErr;
- stsByte = msgByte = (-1); /* Preset "bad" values */
- /*
- * Arbitrate for the scsi bus. This will fail if some other device is
- * accessing the bus at this time (which is unlikely).
- *
- *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
- *** Do not set breakpoints or call any functions that may require ***
- *** I/O (such as display code that accesses font resources between ***
- *** SCSIGet and SCSIComplete, ***
- *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
- */
- for (totalTries = 0; totalTries < kMaxSCSIRetries; totalTries++) {
- for (getTries = 0; getTries < 4; getTries++) {
- /*
- * Wait for the bus to go free.
- */
- watchdog = TickCount() + 300; /* 5 second timeout */
- while (ScsiBusBusy()) {
- if (TickCount() > watchdog) {
- status = scArbNBErr; /* The bus is busy */
- goto exit; /* Fail */
- }
- }
- /*
- * The bus is free, try to grab it
- */
- for (iCount = 0; iCount < 4; iCount++) {
- if ((status = SCSIGet()) == noErr)
- break;
- }
- if (status == noErr) /* If we get the bus, leave */
- break; /* the "getTries" loop */
- /*
- * The bus became busy again. Try to wait for it to go free.
- */
- for (iCount = 0; iCount < 100 && ScsiBusBusy(); iCount++)
- ;
- } /* The getTries loop */
- if (status != noErr) {
- /*
- * If the above loop fails, it means that the SCSI Manager
- * thinks the bus is not busy and not selected, but "someone" has
- * set the internal semaphore that signals that the SCSI Manager
- * itself is busy. The application will have to handle this
- * problem. (We tried getTries * 4 times).
- */
- goto exit;
- }
- /*
- * We now own the SCSI bus. Try to select the device.
- */
- if ((status = SCSISelect(targetID)) != noErr)
- goto exit;
- /*
- * From this point on, we must exit through SCSIComplete() even if an
- * error is detected. Send a command to the selected device. There are
- * several failure modes, including an illegal command (such as a
- * write to a read-only device). If the command failed because of
- * "device busy", we will try it again.
- */
- status = SCSICmd((Ptr) scsiCommand, 6);
- if (status == noErr)
- status = SCSIRead((Ptr) requestTIB);
- finish:
- /*
- * SCSIComplete "runs" the bus-phase algorithm until the bitter end,
- * returning the status and command-completion message bytes..
- */
- completionStatus = SCSIComplete(
- &stsByte,
- &msgByte,
- (5 * 60) /* Five second timeout */
- );
- /*
- * If we have an error here, return as the "final" status.
- *
- */
- if (completionStatus != noErr)
- status = completionStatus;
- else {
- /*
- * ScsiComplete is happy. If the device is busy, Pause for 1/4
- * second and try again.
- */
- if (stsByte == kScsiStatusBusy) {
- Delay(15, &watchdog);
- continue; /* Do next totalTries attempt */
- }
- }
- /*
- * This is the normal exit (success) or final failure exit.
- */
- break;
- } /* totalTries loop */
- exit:
- /*
- * Return an artificial error if the device returns a non-zero status:
- * statusErr Caller should issue RequestSense.
- * controlErr Device is busy (self-test?) try again later.
- * ioErr Something is dreadfully wrong.
- * scPhaseErr If the device returned good status, the user buffer
- * was larger than was needed. (This will be the case
- * for an inquiry or request sense command.) Since the
- * caller can determine the actual length by examining
- * the TIB or the buffer contents, we can return noErr.
- * Also, there is a bug in the combination of System 7.0.1 and the 53C96
- * that may cause the real SCSI Status Byte to be in the Message byte.
- */
- if (stsByte == kScsiStatusGood
- && msgByte == kScsiStatusCheckCondition)
- stsByte = kScsiStatusCheckCondition;
- if (status == scPhaseErr && stsByte == kScsiStatusGood)
- status = noErr;
- if (status == noErr) {
- switch (stsByte) {
- case kScsiStatusGood: break;
- case kScsiStatusCheckCondition: status = statusErr; break;
- case kScsiStatusBusy: status = controlErr; break;
- default: status = ioErr; break;
- }
- }
- return (status);
- }
-